import * as fs from 'fs';
import * as path from 'path';
import { ApiEndpoint, ApiParameter, ApiExample } from '../extension';

export class GraphQLParser {
  async parseFile(filePath: string): Promise<ApiEndpoint[]> {
    const content = fs.readFileSync(filePath, 'utf8');
    const lines = content.split('\n');
    const endpoints: ApiEndpoint[] = [];

    // Parse GraphQL schema files (.graphql, .gql, .graphqls)
    if (this.isSchemaFile(filePath)) {
      const schemaEndpoints = await this.parseSchemaFile(content, filePath);
      endpoints.push(...schemaEndpoints);
    }

    // Parse GraphQL query files
    if (this.isQueryFile(filePath)) {
      const queryEndpoints = await this.parseQueryFile(content, filePath);
      endpoints.push(...queryEndpoints);
    }

    return endpoints;
  }

  private isSchemaFile(filePath: string): boolean {
    const ext = path.extname(filePath).toLowerCase();
    return ext === '.graphql' || ext === '.gql' || ext === '.graphqls';
  }

  private isQueryFile(filePath: string): boolean {
    const ext = path.extname(filePath).toLowerCase();
    return ext === '.graphql' || ext === '.gql';
  }

  private async parseSchemaFile(content: string, filePath: string): Promise<ApiEndpoint[]> {
    const endpoints: ApiEndpoint[] = [];
    const lines = content.split('\n');

    // Parse type definitions
    for (let i = 0; i < lines.length; i++) {
      const line = lines[i].trim();
      
      // Look for type definitions
      if (line.startsWith('type ') && line.includes('{')) {
        const typeEndpoint = this.parseTypeDefinition(lines, i, filePath);
        if (typeEndpoint) {
          endpoints.push(typeEndpoint);
        }
      }
      
      // Look for input type definitions
      if (line.startsWith('input ') && line.includes('{')) {
        const inputEndpoint = this.parseInputDefinition(lines, i, filePath);
        if (inputEndpoint) {
          endpoints.push(inputEndpoint);
        }
      }
      
      // Look for enum definitions
      if (line.startsWith('enum ') && line.includes('{')) {
        const enumEndpoint = this.parseEnumDefinition(lines, i, filePath);
        if (enumEndpoint) {
          endpoints.push(enumEndpoint);
        }
      }
    }

    return endpoints;
  }

  private async parseQueryFile(content: string, filePath: string): Promise<ApiEndpoint[]> {
    const endpoints: ApiEndpoint[] = [];
    const lines = content.split('\n');

    for (let i = 0; i < lines.length; i++) {
      const line = lines[i].trim();
      
      // Look for queries
      if (line.startsWith('query ') || line.startsWith('mutation ') || line.startsWith('subscription ')) {
        const queryEndpoint = this.parseQueryDefinition(lines, i, filePath);
        if (queryEndpoint) {
          endpoints.push(queryEndpoint);
        }
      }
    }

    return endpoints;
  }

  private parseTypeDefinition(lines: string[], startLine: number, filePath: string): ApiEndpoint | null {
    const line = lines[startLine];
    const typeMatch = line.match(/type\s+(\w+)\s*\{/);
    if (!typeMatch) return null;

    const typeName = typeMatch[1];
    let description = '';
    let parameters: ApiParameter[] = [];

    // Look for description above the type definition
    for (let i = startLine - 1; i >= Math.max(0, startLine - 5); i--) {
      const commentLine = lines[i];
      if (commentLine.includes('"""') || commentLine.includes("'''")) {
        description = this.extractGraphQLDescription(lines, i);
        break;
      }
    }

    // Parse fields in the type definition
    let inType = false;
    for (let i = startLine + 1; i < lines.length; i++) {
      const fieldLine = lines[i].trim();
      
      if (fieldLine.includes('}')) {
        break;
      }
      
      if (fieldLine && !fieldLine.startsWith('#') && !fieldLine.startsWith('"""')) {
        const fieldMatch = fieldLine.match(/^(\w+)\s*:\s*([^!]+)(!?)(\s*#\s*(.+))?$/);
        if (fieldMatch) {
          const fieldName = fieldMatch[1];
          const fieldType = fieldMatch[2].trim();
          const isRequired = fieldMatch[3] === '!';
          const fieldDescription = fieldMatch[4] ? fieldMatch[4].trim() : `Field ${fieldName}`;

          parameters.push({
            name: fieldName,
            type: fieldType,
            description: fieldDescription,
            required: isRequired,
            source: 'field'
          });
        }
      }
    }

    const examples = this.generateGraphQLExamples('TYPE', typeName, parameters);

    return {
      name: typeName,
      path: `/graphql/type/${typeName}`,
      method: 'TYPE',
      description: description || `GraphQL type definition for ${typeName}`,
      parameters,
      returnType: typeName,
      returnDescription: `GraphQL type ${typeName}`,
      examples,
      filePath,
      lineNumber: startLine + 1
    };
  }

  private parseInputDefinition(lines: string[], startLine: number, filePath: string): ApiEndpoint | null {
    const line = lines[startLine];
    const inputMatch = line.match(/input\s+(\w+)\s*\{/);
    if (!inputMatch) return null;

    const inputName = inputMatch[1];
    let description = '';
    let parameters: ApiParameter[] = [];

    // Look for description above the input definition
    for (let i = startLine - 1; i >= Math.max(0, startLine - 5); i--) {
      const commentLine = lines[i];
      if (commentLine.includes('"""') || commentLine.includes("'''")) {
        description = this.extractGraphQLDescription(lines, i);
        break;
      }
    }

    // Parse fields in the input definition
    for (let i = startLine + 1; i < lines.length; i++) {
      const fieldLine = lines[i].trim();
      
      if (fieldLine.includes('}')) {
        break;
      }
      
      if (fieldLine && !fieldLine.startsWith('#') && !fieldLine.startsWith('"""')) {
        const fieldMatch = fieldLine.match(/^(\w+)\s*:\s*([^!]+)(!?)(\s*#\s*(.+))?$/);
        if (fieldMatch) {
          const fieldName = fieldMatch[1];
          const fieldType = fieldMatch[2].trim();
          const isRequired = fieldMatch[3] === '!';
          const fieldDescription = fieldMatch[4] ? fieldMatch[4].trim() : `Input field ${fieldName}`;

          parameters.push({
            name: fieldName,
            type: fieldType,
            description: fieldDescription,
            required: isRequired,
            source: 'input'
          });
        }
      }
    }

    const examples = this.generateGraphQLExamples('INPUT', inputName, parameters);

    return {
      name: inputName,
      path: `/graphql/input/${inputName}`,
      method: 'INPUT',
      description: description || `GraphQL input type definition for ${inputName}`,
      parameters,
      returnType: inputName,
      returnDescription: `GraphQL input type ${inputName}`,
      examples,
      filePath,
      lineNumber: startLine + 1
    };
  }

  private parseEnumDefinition(lines: string[], startLine: number, filePath: string): ApiEndpoint | null {
    const line = lines[startLine];
    const enumMatch = line.match(/enum\s+(\w+)\s*\{/);
    if (!enumMatch) return null;

    const enumName = enumMatch[1];
    let description = '';
    let parameters: ApiParameter[] = [];

    // Look for description above the enum definition
    for (let i = startLine - 1; i >= Math.max(0, startLine - 5); i--) {
      const commentLine = lines[i];
      if (commentLine.includes('"""') || commentLine.includes("'''")) {
        description = this.extractGraphQLDescription(lines, i);
        break;
      }
    }

    // Parse enum values
    for (let i = startLine + 1; i < lines.length; i++) {
      const valueLine = lines[i].trim();
      
      if (valueLine.includes('}')) {
        break;
      }
      
      if (valueLine && !valueLine.startsWith('#') && !valueLine.startsWith('"""')) {
        const valueMatch = valueLine.match(/^(\w+)(\s*#\s*(.+))?$/);
        if (valueMatch) {
          const valueName = valueMatch[1];
          const valueDescription = valueMatch[2] ? valueMatch[2].trim() : `Enum value ${valueName}`;

          parameters.push({
            name: valueName,
            type: enumName,
            description: valueDescription,
            required: true,
            source: 'enum'
          });
        }
      }
    }

    const examples = this.generateGraphQLExamples('ENUM', enumName, parameters);

    return {
      name: enumName,
      path: `/graphql/enum/${enumName}`,
      method: 'ENUM',
      description: description || `GraphQL enum definition for ${enumName}`,
      parameters,
      returnType: enumName,
      returnDescription: `GraphQL enum ${enumName}`,
      examples,
      filePath,
      lineNumber: startLine + 1
    };
  }

  private parseQueryDefinition(lines: string[], startLine: number, filePath: string): ApiEndpoint | null {
    const line = lines[startLine];
    const queryMatch = line.match(/^(query|mutation|subscription)\s+(\w+)(\s*\(([^)]*)\))?\s*\{/);
    if (!queryMatch) return null;

    const operationType = queryMatch[1].toUpperCase();
    const operationName = queryMatch[2];
    const parameters = queryMatch[3] ? this.parseQueryParameters(queryMatch[3]) : [];
    
    let description = '';
    let returnType = 'Object';

    // Look for description above the query definition
    for (let i = startLine - 1; i >= Math.max(0, startLine - 5); i--) {
      const commentLine = lines[i];
      if (commentLine.includes('"""') || commentLine.includes("'''")) {
        description = this.extractGraphQLDescription(lines, i);
        break;
      }
    }

    // Parse the query body to determine return type
    let inQuery = false;
    for (let i = startLine + 1; i < lines.length; i++) {
      const queryLine = lines[i].trim();
      
      if (queryLine.includes('}')) {
        break;
      }
      
      if (queryLine && !queryLine.startsWith('#') && !queryLine.startsWith('"""')) {
        // Look for field selections
        const fieldMatch = queryLine.match(/^(\w+)(\s*\{)?$/);
        if (fieldMatch) {
          returnType = fieldMatch[1];
          break;
        }
      }
    }

    const examples = this.generateGraphQLExamples(operationType, operationName, parameters);

    return {
      name: operationName,
      path: `/graphql/${operationType.toLowerCase()}/${operationName}`,
      method: operationType,
      description: description || `GraphQL ${operationType.toLowerCase()} operation ${operationName}`,
      parameters,
      returnType,
      returnDescription: `GraphQL ${operationType.toLowerCase()} result`,
      examples,
      filePath,
      lineNumber: startLine + 1
    };
  }

  private parseQueryParameters(paramString: string): ApiParameter[] {
    const params: ApiParameter[] = [];
    const paramList = paramString.split(',').map(p => p.trim());
    
    for (const param of paramList) {
      if (param && param !== '') {
        const paramMatch = param.match(/^(\w+)\s*:\s*([^!]+)(!?)(\s*#\s*(.+))?$/);
        if (paramMatch) {
          const paramName = paramMatch[1];
          const paramType = paramMatch[2].trim();
          const isRequired = paramMatch[3] === '!';
          const paramDescription = paramMatch[4] ? paramMatch[4].trim() : `Parameter ${paramName}`;

          params.push({
            name: paramName,
            type: paramType,
            description: paramDescription,
            required: isRequired,
            source: 'query'
          });
        }
      }
    }
    
    return params;
  }

  private extractGraphQLDescription(lines: string[], startLine: number): string {
    let description = '';
    let inDescription = false;
    
    for (let i = startLine; i < lines.length; i++) {
      const line = lines[i];
      
      if (line.includes('"""') || line.includes("'''")) {
        if (!inDescription) {
          inDescription = true;
          const content = line.replace(/^.*["']{3}/, '').replace(/["']{3}.*$/, '').trim();
          if (content) description += content + ' ';
        } else {
          break;
        }
      } else if (inDescription) {
        description += line.trim() + ' ';
      }
    }
    
    return description.trim();
  }

  private generateGraphQLExamples(operationType: string, operationName: string, parameters: ApiParameter[]): ApiExample[] {
    const examples: ApiExample[] = [];

    // GraphQL query example
    let graphqlExample = '';
    if (operationType === 'TYPE' || operationType === 'INPUT' || operationType === 'ENUM') {
      graphqlExample = `# ${operationType} definition\n`;
      graphqlExample += `${operationType.toLowerCase()} ${operationName} {\n`;
      parameters.forEach(param => {
        const required = param.required ? '!' : '';
        graphqlExample += `  ${param.name}: ${param.type}${required}\n`;
      });
      graphqlExample += `}`;
    } else {
      // Query/Mutation/Subscription
      graphqlExample = `${operationType.toLowerCase()} ${operationName}`;
      if (parameters.length > 0) {
        graphqlExample += `(\n`;
        parameters.forEach(param => {
          const required = param.required ? '!' : '';
          graphqlExample += `  $${param.name}: ${param.type}${required}\n`;
        });
        graphqlExample += `)`;
      }
      graphqlExample += ` {\n`;
      graphqlExample += `  # Query fields here\n`;
      graphqlExample += `}`;
    }

    examples.push({
      title: 'GraphQL Schema',
      description: `GraphQL ${operationType.toLowerCase()} definition`,
      code: graphqlExample,
      language: 'graphql'
    });

    // HTTP POST example for queries/mutations
    if (operationType === 'QUERY' || operationType === 'MUTATION') {
      let httpExample = `curl -X POST "http://localhost:4000/graphql" \\\n`;
      httpExample += `  -H "Content-Type: application/json" \\\n`;
      httpExample += `  -d '{\n`;
      httpExample += `    "query": "${operationType.toLowerCase()} ${operationName}`;
      
      if (parameters.length > 0) {
        httpExample += `(\n`;
        parameters.forEach(param => {
          const required = param.required ? '!' : '';
          httpExample += `      $${param.name}: ${param.type}${required}\n`;
        });
        httpExample += `    )`;
      }
      
      httpExample += ` {\n`;
      httpExample += `      # Query fields here\n`;
      httpExample += `    }\n`;
      httpExample += `  }'`;

      examples.push({
        title: 'HTTP POST Request',
        description: `HTTP POST request to GraphQL endpoint`,
        code: httpExample,
        language: 'bash'
      });
    }

    return examples;
  }
} 